package com.wex.app.tools;

import ohos.media.codec.Codec;
import ohos.media.common.BufferInfo;

import java.nio.ByteBuffer;

import static ohos.media.codec.Codec.createDecoder;
import static ohos.media.codec.Codec.createEncoder;

public class CodecUtils {

    public enum Type {
        ENCODE,
        DECODE,
    }

    public enum Format {
        H264,
        H265,
    }

    public interface Callback {
        //返回实际写入数据量
        int usrWriteSysRead(byte[] d);
        void usrReadSysWrite(byte[] d);
        //编解码开始前/结束后回调(注意非UI线程)
        void onStart();
        void onStop();
    }

    public int create(CodecUtils.Type t, CodecUtils.Format f, int width, int height, int fps, CodecUtils.Callback c) {
        if (c == null)
            return -1;

        //上次任务未结束
        if (state != State.STATE_IDLE)
            return -2;

        //把配置记录到format,供后续反复create
        format = new ohos.media.common.Format();
        if (f == CodecUtils.Format.H264)
            format.putStringValue(ohos.media.common.Format.MIME, ohos.media.common.Format.VIDEO_AVC);
        else
            format.putStringValue(ohos.media.common.Format.MIME, ohos.media.common.Format.VIDEO_HEVC);
        format.putIntValue(ohos.media.common.Format.WIDTH, width);
        format.putIntValue(ohos.media.common.Format.HEIGHT, height);
        format.putIntValue(ohos.media.common.Format.FRAME_RATE, fps);
        format.putIntValue(ohos.media.common.Format.BITRATE_MODE, 1);
        format.putIntValue(ohos.media.common.Format.FRAME_INTERVAL, 1);
        if (t == CodecUtils.Type.ENCODE) {
            format.putIntValue(ohos.media.common.Format.COLOR_MODEL, 21);
            format.putIntValue(ohos.media.common.Format.BIT_RATE, 10000000);
        }

        //其他参数备份
        type = t;
        callback = c;
        timeStampDiv = 1000000 / fps;
        return 0;
    }

    public int start() {
        if (state == State.STATE_RUNNING)
            return 0;

        //上次任务未结束
        if (state != State.STATE_IDLE)
            return -1;

        //编解码器创建、注册监听、配置格式、start
        if (type == Type.ENCODE)
            codec = createEncoder();
        else
            codec = createDecoder();
        if (codec == null)
            return -2;

        if (!codec.registerCodecListener(listenData)) {
            codec.release();
            codec = null;
            return -3;
        }

        if (!codec.setCodecFormat(format)) {
            codec.release();
            codec = null;
            return -4;
        }

        if (!codec.start()) {
            codec.release();
            codec = null;
            return -5;
        }

        //更新状态,开线程推数据
        state = State.STATE_RUNNING;
        Thread thread = new Thread(pushData);
        thread.start();
        return 0;
    }

    public int stop() {
        if (state != State.STATE_RUNNING)
            return 0;

        if (codec == null)
            return -1;

        state = State.STATE_CLOSING;
        return 0;
    }

    //本地参数备份
    private CodecUtils.Type type;
    private CodecUtils.Callback callback;
    private ohos.media.common.Format format;
    private Codec codec = null;
    private int timeStampDiv;

    //线程状态同步
    private enum State {STATE_IDLE, STATE_RUNNING, STATE_CLOSING};
    private volatile State state = State.STATE_IDLE;

    //编解码器数据就绪监听
    private Codec.ICodecListener listenData = new Codec.ICodecListener() {
        @Override
        public void onReadBuffer(ByteBuffer byteBuffer, BufferInfo bufferInfo, int i) {
            if (bufferInfo.size > 0) {
                byte[] bytes = new byte[bufferInfo.size];
                byteBuffer.get(bytes, bufferInfo.offset, bufferInfo.size);
                callback.usrReadSysWrite(bytes);
            }
        }
        @Override
        public void onError(int i, int i1, int i2) {}
    };

    //往编解码推数据线程
    private Runnable pushData = new Runnable() {
        @Override
        public void run() {
            callback.onStart();

            BufferInfo info = new BufferInfo();
            while (state == State.STATE_RUNNING && codec != null) {
                //阻塞
                ByteBuffer buff = codec.getAvailableBuffer(-1);
                if (buff != null && state == State.STATE_RUNNING) {
                    buff.clear();

                    //获取需求数据量信息
                    info.bufferType = BufferInfo.BUFFER_TYPE_PARTIAL_FRAME;
                    info.offset = 0;
                    info.size = buff.capacity();
                    info.timeStamp += timeStampDiv;

                    //向使用者请求数据
                    byte[] bytes = new byte[buff.capacity()];
                    if (state == State.STATE_RUNNING)
                        info.size = callback.usrWriteSysRead(bytes);

                    //往编/解码器推数据
                    if (info.size > 0) {
                        buff.put(bytes);
                        codec.writeBuffer(buff, info);
                    }
                }
            }

            //codec销毁
            codec.stop();
            codec.release();
            codec = null;

            callback.onStop();

            //线程任务结束标志,允许开启下一轮编解码
            state = State.STATE_IDLE;
        }
    };

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        if (state != State.STATE_IDLE)
            state = State.STATE_CLOSING;
    }
}
